package com.sayice.coursetable.logic;

import android.util.Log;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.sayice.coursetable.CourseTableApp;
import com.sayice.coursetable.logic.dao.AppDatabase;
import com.sayice.coursetable.logic.dao.CourseDao;
import com.sayice.coursetable.logic.dao.CourseTableDao;
import com.sayice.coursetable.logic.model.Course;
import com.sayice.coursetable.logic.model.CourseTable;
import com.sayice.coursetable.logic.model.Exam;
import com.sayice.coursetable.logic.model.Score;
import com.sayice.coursetable.logic.network.CourseService;
import com.sayice.coursetable.logic.network.ExamService;
import com.sayice.coursetable.logic.network.MajorService;
import com.sayice.coursetable.logic.network.ScoreService;
import com.sayice.coursetable.logic.network.WeekService;
import com.sayice.coursetable.logic.utils.CourseUtils;
import com.sayice.coursetable.logic.utils.DateUtils;
import com.sayice.coursetable.logic.utils.FileUtils;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;

public abstract class Repository {
    private static final CourseTableDao courseTableDao
            = AppDatabase.getInstance(CourseTableApp.context).courseTableDao();
    private static final CourseDao courseDao
            = AppDatabase.getInstance(CourseTableApp.context).courseDao();

    static {
        // 初始化 CourseTable 表的数据
        if (courseTableDao.count() == 0) {
            courseTableDao.insertAll(new CourseTable(), new CourseTable());
        }
    }

    /**
     * 导入个人课表
     *
     * @param tableId  课表编号
     * @param username 用户名
     * @param password 密码
     * @return 导入成功返回 true，导入失败返回 false
     */
    public static boolean importPersonalCourse(int tableId, String username, String password) {
        // 更新教学周历
        saveWeekCalendar(username, password);
        Document doc = CourseService.getPersonalCoursePage(username, password);
        if (doc == null) {
            Log.d("Repository", "获取课程页面失败");
            return false;
        }
        // 删除原来的课程
        courseDao.deleteOfTable(tableId);
        // 更新课表名
        String owner = doc.select("div[id=Top1_divLoginName]").text();
        courseTableDao.updateName(tableId, "".equals(owner) ? "课表" : owner);
        // 解析课程数据，存入数据库
        Elements courseElements = doc.select("div[class=kbcontent]");
        for (Element courseElement : courseElements) {
            if (courseElement.childNodeSize() > 1) {
                Log.v("Repository", courseElement.html());
                String content = courseElement.html();
                Course course = new Course();
                course.tableId = tableId;
                course.name = content.substring(0, content.indexOf('<'));
                Elements teacherElements = courseElement.select("font[title=老师]");
                Elements weekElements = courseElement.select("font[title=周次(节次)]");
                Elements roomElements = courseElement.select("font[title=教室]");
                if (teacherElements.size() == 0 || weekElements.size() == 0 || roomElements.size() == 0) {
                    return false;
                }
                course.teach = teacherElements.get(0).text();
                course.weeks = weekElements.get(0).text();
                course.room = roomElements.get(0).text();
                String id = courseElement.id();
                course.dayNum = id.charAt(id.indexOf('-') + 1) - '0';
                course.sectionNum = (courseElement.parent().parent().child(0).text().charAt(1) - '0' + 1) / 2;
                Log.d("Repository", "插入课程 id: " + courseDao.insert(course));

                int lineIndex = content.indexOf("-----");
                if (lineIndex != -1) {
                    Course course2 = new Course();
                    course2.tableId = tableId;
                    String content2 = content.substring(lineIndex);
                    content2 = content2.substring(content2.indexOf('>') + 1);
                    course2.name = content2.substring(0, content2.indexOf('<')).trim();
                    course2.teach = courseElement.select("font[title=老师]").get(1).text();
                    course2.weeks = courseElement.select("font[title=周次(节次)]").get(1).text();
                    course2.room = courseElement.select("font[title=教室]").get(1).text();
                    course2.dayNum = course.dayNum;
                    course2.sectionNum = course.sectionNum;
                    Log.d("Repository", "插入课程 id: " + courseDao.insert(course2));
                }
            }
        }
        Log.d("Repository", "导入课表成功");
        return true;
    }

    /**
     * 导入班级课表
     *
     * @param tableId  课表编号
     * @param username 用户名
     * @param password 密码
     * @param grade    年级（入学年份）
     * @param major    专业
     * @param classNum 班级
     * @return 导入成功返回 true，导入失败返回 false
     */
    public static boolean importClassCourse(int tableId, String username, String password, int grade, String major, int classNum) {
        // 更新教学周历和专业列表
        saveWeekCalendar(username, password);
        saveMajors(username, password, grade);
        String majorCode = getMajorCode(grade, major);
        if (majorCode == null) {
            Log.d("Repository", "获取专业代号失败");
            return false;
        }
        Document doc = CourseService.getClassCoursePage(username, password, grade, majorCode, classNum);
        if (doc == null) {
            Log.d("Repository", "获取班级课程页面失败");
            return false;
        }
        // 删除原来的课程
        courseDao.deleteOfTable(tableId);
        // 更新课表名
        courseTableDao.updateName(tableId, grade + major + classNum + "班");
        // 解析课程数据，存入数据库
        Elements courseElements = doc.select("div[class=kbcontent1]");
        for (Element courseElement : courseElements) {
            if (courseElement.childNodeSize() > 0) {
                Log.v("Repository", courseElement.html());
                String content = courseElement.html();
                String[] info = content.split("<br>");
                if (info.length != 3) {
                    return false;
                }
                Course course = new Course();
                course.tableId = tableId;
                course.name = info[0];
                String teacherAndWeeks = info[1];
                int splitIndex = teacherAndWeeks.indexOf('(');
                course.teach = teacherAndWeeks.substring(0, splitIndex);
                course.weeks = teacherAndWeeks.substring(splitIndex);
                course.room = info[2];
                int index = courseElement.parent().elementSiblingIndex();
                course.dayNum = (int) Math.ceil((double) index / 6);
                int i = index % 6;
                course.sectionNum = i == 0 ? 6 : i;
                Log.d("Repository", "插入课程 id: " + courseDao.insert(course) + ", 节号: " + course.sectionNum + "星期号：" + course.dayNum);
            }
        }
        Log.d("Repository", "导入班级课表成功");
        return true;
    }

    /**
     * 导入教师课表
     *
     * @param tableId     课表编号
     * @param username    用户名
     * @param password    密码
     * @param teacherName 教师名
     * @return 导入成功返回 true，导入失败返回 false
     */
    public static boolean importTeacherCourse(int tableId, String username, String password, String teacherName) {
        // 更新教学周历
        saveWeekCalendar(username, password);
        Document doc = CourseService.getTeacherCoursePage(username, password, teacherName);
        if (doc == null) {
            Log.d("Repository", "获取教师课程页面失败");
            return false;
        }
        // 删除原来的课程
        courseDao.deleteOfTable(tableId);
        // 更新课表名
        courseTableDao.updateName(tableId, teacherName + "老师");
        // 解析课程数据，存入数据库
        Elements courseElements = doc.select("div[class=kbcontent1]");
        for (Element courseElement : courseElements) {
            if (courseElement.childNodeSize() > 0) {
                Log.v("Repository", courseElement.html());
                String content = courseElement.html();
                String[] info = content.split("<br>");
                if (info.length != 3) {
                    return false;
                }
                Course course = new Course();
                course.tableId = tableId;
                course.name = info[0];
                String teacherAndWeeks = info[1];
                int splitIndex = teacherAndWeeks.indexOf('(');
                course.teach = teacherAndWeeks.substring(0, splitIndex);
                course.weeks = teacherAndWeeks.substring(splitIndex);
                course.room = info[2];
                int index = courseElement.parent().elementSiblingIndex();
                course.dayNum = (int) Math.ceil((double) index / 6);
                int i = index % 6;
                course.sectionNum = i == 0 ? 6 : i;
                Log.d("Repository", "插入课程 id: " + courseDao.insert(course) + ", 节号: " + course.sectionNum + "星期号：" + course.dayNum);
            }
        }
        Log.d("Repository", "导入教师课表成功");
        return true;
    }

    /**
     * 获取成绩
     *
     * @param username 用户名
     * @param password 密码
     * @return 返回成绩列表，若获取失败则返回 null
     */
    public static List<Score> getScores(String username, String password) {
        // 更新教学周历
        saveWeekCalendar(username, password);
        Document doc = ScoreService.getScorePage(username, password);
        if (doc == null) {
            Log.d("Repository", "获取成绩页面失败");
            return null;
        }
        List<Score> scores = new ArrayList<>();
        Elements scoreElements = doc.select("tr");
        for (Element scoreElement : scoreElements) {
            if (scoreElement.children().size() == 13) {
                Score score = new Score();
                score.courseName = scoreElement.child(3).text();
                score.mark = scoreElement.child(4).text();
                score.credit = Float.parseFloat(scoreElement.child(5).text());
                score.gradePoint = Float.parseFloat(scoreElement.child(7).text());
                score.type = scoreElement.child(12).text();
                score.term = scoreElement.child(1).text();
                scores.add(score);
            }
        }
        return scores;
    }

    /**
     * 获取考试安排
     *
     * @param username 用户名
     * @param password 密码
     * @return 返回考试列表，若获取失败则返回 null
     */
    public static List<Exam> getExams(String username, String password) {
        // 更新教学周历
        saveWeekCalendar(username, password);
        Document doc = ExamService.getExamPage(username, password, getCurrentTerm());
        if (doc == null) {
            Log.d("Repository", "获取考试安排页面失败");
            return null;
        }
        List<Exam> exams = new ArrayList<>();
        Elements examElements = doc.select("tr:gt(0)");
        for (Element examElement : examElements) {
            if (examElement.children().size() == 9) {
                Exam exam = new Exam();
                exam.courseName = examElement.child(3).text();
                exam.time = examElement.child(4).text();
                exam.room = examElement.child(5).text();
                exams.add(exam);
            }
        }
        return exams;
    }

    /**
     * 获取课表名
     *
     * @param tableId 课表编号
     * @return 返回课表名
     */
    public static String getTableName(int tableId) {
        return courseTableDao.getName(tableId);
    }

    /**
     * 获取当前学期
     *
     * @return 返回当前学期字符串，若获取失败则返回 null
     */
    public static String getCurrentTerm() {
        String weekHtml = FileUtils.getText("weekCalendar.html");
        if (weekHtml == null) {
            return null;
        }
        Document doc = Jsoup.parseBodyFragment(weekHtml);
        Elements termElements = doc.select("select[id=xnxq01id]>option[selected]");
        if (termElements.size() == 0) {
            return null;
        }
        return termElements.get(0).text();
    }

    /**
     * 获取当前周次（根据系统时间）
     *
     * @return 返回当前周次，若获取失败则返回 -1
     */
    public static int getCurrentWeek() {
        String weekHtml = FileUtils.getText("weekCalendar.html");
        if (weekHtml == null) {
            return -1;
        }
        Document doc = Jsoup.parseBodyFragment(weekHtml);
        // 解析当前周次
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy年MM月dd", Locale.CHINESE);
        Date date = new Date(System.currentTimeMillis());
        String dateStr = dateFormat.format(date);
        Elements dayElements = doc.select("td[title=" + dateStr + "]");
        if (dayElements.size() == 0) {
            return -1;
        }
        String week = dayElements.get(0).firstElementSibling().text();
        return Integer.parseInt(week);
    }

    /**
     * 获取某周的日期
     *
     * @param week 周次
     * @return 返回 [[月, 日], [月, 日], [月, 日] ... ]，若获取失败则返回 null
     */
    public static String[][] getWeekDates(int week) {
        String weekHtml = FileUtils.getText("weekCalendar.html");
        if (weekHtml == null) {
            return null;
        }
        Document doc = Jsoup.parseBodyFragment(weekHtml);
        Elements tableElements = doc.select("table[id=kbtable]");
        if (tableElements.size() == 0) {
            return null;
        }
        Elements weekDayElements = tableElements.get(0).child(0)
                .select("tr:nth-child(" + (week + 1) + ")");
        if (weekDayElements.size() == 0) {
            return null;
        }
        String[][] dates = new String[7][2];
        Element dayElement = weekDayElements.get(0).child(1);
        for (String[] date : dates) {
            String dateStr = dayElement.attr("title");
            int index1 = dateStr.indexOf('年');
            int index2 = dateStr.indexOf('月');
            String month = dateStr.substring(index1 + 1, index2);
            String day = dateStr.substring(index2 + 1);
            date[0] = month;
            date[1] = day;
            dayElement = dayElement.nextElementSibling();
        }
        return dates;
    }

    /**
     * 获取周课程
     *
     * @param tableId 课表编号
     * @param week    周次
     */
    public static Course[] getWeekCourses(int tableId, int week) {
        final int maxSize = 42;
        List<Course> courses = courseDao.loadAll(tableId);
        List<Course> weekCourses = CourseUtils.findForWeek(courses, week);
        Course[] courseList = new Course[maxSize];
        for (Course course : weekCourses) {
            int index = 7 * (course.sectionNum - 1) + course.dayNum - 1;
            if (index < maxSize) {
                courseList[index] = course;
            }
        }
        return courseList;
    }

    /**
     * 获取当日课程
     *
     * @param tableId 课表编号
     */
    public static List<Course> getDayCourses(int tableId) {
        List<Course> courses = courseDao.loadByDayNum(tableId, DateUtils.getWeekDayNum());
        return CourseUtils.findForWeek(courses, getCurrentWeek());
//        List<Course> courses = courseDao.loadByDayNum(tableId, 2);
//        return CourseUtils.findForWeek(courses, 15);
    }

    /**
     * 获取专业代号
     *
     * @param grade 年级（入学年份）
     * @param major 专业
     * @return 返回专业代号，若获取失败则返回 null
     */
    private static String getMajorCode(int grade, String major) {
        String majorsJson = FileUtils.getText(grade + "majors.json");
        JSONArray majorsArray = JSON.parseArray(majorsJson);
        if (majorsArray == null) {
            return null;
        }
        for (Object m : majorsArray) {
            JSONObject majorJson = (JSONObject) m;
            if (majorJson.getString("dmmc").equals(major)) {
                return majorJson.getString("dm");
            }
        }
        return null;
    }

    /**
     * 保存教学周历
     *
     * @param username 用户名
     * @param password 密码
     */
    private static void saveWeekCalendar(String username, String password) {
        String weekCalendarHtml = WeekService.getWeekCalendar(username, password);
        if (weekCalendarHtml == null) {
            Log.d("Repository", "获取教学周历页面失败");
            return;
        }
        FileUtils.saveText("weekCalendar.html", weekCalendarHtml);
    }

    /**
     * 保存专业列表
     *
     * @param username 用户名
     * @param password 密码
     * @param grade    年级（入学年份）
     */
    private static void saveMajors(String username, String password, int grade) {
        String majorsJson = MajorService.getMajors(username, password, grade);
        if (majorsJson == null) {
            Log.d("Repository", "获取专业列表失败");
            return;
        }
        FileUtils.saveText(grade + "majors.json", majorsJson);
    }
}
